from direct.actor.Actor import Actor
from direct.showbase.DirectObject import DirectObject
from panda3d.core import NodePath, PandaNode
from panda3d.physics import *
from pandac.PandaModules import *

class Player(DirectObject):
    def __init__(self):
        # Control attributes
        self._weight = 30
        self.dead = False
        
        self._forwardScale = -3
        self._upwardScale = 1
        self._brakeScalar = 0
        self._brakeMin = 0
        self._brakeMax = 100
        self._angleMin = 0
        self._angleMax = 70
        
        # Control - Gravity Compensation
        self.upwardForce = LinearVectorForce(0, 0, 0)
        #self.upwardForce.setMassDependent(1)
        self.forceNode = ForceNode('PlayerNodeForce')
        self.forceNode.addForce(self.upwardForce)
        
        # Physics
        self._actorNode = ActorNode('PlayerNodePhysics')
        #self._actorNode.getPhysicsObject().setMass(self._weight)
        self._actorNode.getPhysical(0).addLinearForce(self.upwardForce)
        #base.physicsMgr.attachPhysicalNode(self._actorNode)
        
        # Main nodepath node path
        self.nodepath = NodePath('PlayerNodePath').attachNewNode(self._actorNode)
        self.nodepath.reparentTo(render)
        
        # Model
        self._actorModel = Actor("models/scrat.dae")
        self._actorModel.reparentTo(self.nodepath)
        self._actorModel.setPos(0, 0, 0)
        self._actorModel.setScale(0.05)
        self._actorModel.setH(90)
        #self._acornModel.setTwoSided(True)
        #self._acornModel.setDepthWrite(True)
        
        # Collision
        self.collider = self._actorModel.attachNewNode(CollisionNode('PlayerCollision'))
        self.collider.node().addSolid(CollisionSphere(0, 0, 6, 7))
        #self.collider.show() # Debugging
        base.cTrav.addCollider(self.collider, base.collHandler)
        base.collHandler.addCollider(self.collider, self.nodepath)
        
        # A little pre-calculation
        self._angleCoefA = float(self._angleMax - self._angleMin) / self._brakeMax
        self._angleCoefB = float(self._angleMin)
        
        # Player movement task
        taskMgr.add(self.MovementTask, "PlayerMovementTask", appendTask=True)
    ############################################################################
    def Start(self):
        base.physicsMgr.attachPhysicalNode(self._actorNode)
    ############################################################################
    def BrakeAmmount(self, brakeScalar):
        if(brakeScalar):
            self._brakeScalar = brakeScalar;
        
        return self._brakeScalar
    ############################################################################
    def MovementTask(self, task):
        if self.dead:
            return task.cont
        
        # Forward speed -> Use 'Fluid' to prevent collision detection problems
        self.nodepath.setFluidY(self.nodepath, self._forwardScale * self._brakeScalar * globalClock.getDt())

        # Upward force
        self._actorNode.getPhysical(0).removeLinearForce(self.upwardForce)
        self.forceNode.removeForce(self.upwardForce)
        force = self._upwardScale * (self._brakeScalar - 10)
        self.upwardForce = LinearVectorForce(0, 0, force)
        #self.upwardForce.setMassDependent(1)
        self.forceNode.addForce(self.upwardForce)
        self._actorNode.getPhysical(0).addLinearForce(self.upwardForce)
        
        # Linear transform from brake scalar to player angle
        roll = (self._angleCoefA * self._brakeScalar + self._angleCoefB) * -1.0 * 0.5
        self._actorModel.setR(roll)
        
        return task.cont
    ############################################################################
    